/******************************************************************************
 * Copyright (C) 2025 TTLzi.
 * 
 * All Rights Reserved.
 * 
 * @file    w25qxx.c
 *  
 * @par     dependencies
 *			"w25qxx.h"
 *			"spi.h"
 *			"gpio.h"
 * 
 * @author  TTLzi
 * 
 * @brief   W25QXX简易驱动源文件
 * 
 * @version V1.0
 * 
 * @date	2025.09.30
 * 
 * @note    1 tab = 4 spaces
******************************************************************************/
/******************************** Includes ***********************************/
#include "w25qxx.h"

#include "spi.h"
#include "gpio.h"
/******************************** Includes ***********************************/

/******************************** Defines  ***********************************/
/******************************** Defines  ***********************************/

/******************************** Declare ************************************/
/******************************** Declare ************************************/

/******************************** Variables **********************************/
static uint8_t _w25qxx_buf[4096] = {0};
/******************************** Variables **********************************/

/******************************** Functions **********************************/
void 		_w25qxx_set_nss				(uint8_t level)
{
	HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,level ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

void		_w25qxx_write_byte			(uint8_t byte)
{
	HAL_SPI_Transmit(&hspi1,&byte,1,100);
}

uint8_t		_w25qxx_read_byte			(void)
{
	uint8_t byte = 0;
	HAL_SPI_Receive(&hspi1,&byte,1,100);
	return byte;
}

uint8_t 	_w25qxx_write_data			(uint8_t * const pdata,uint16_t size)
{
	HAL_SPI_Transmit(&hspi1,pdata,size,100);
	return 0;
}

uint8_t		_w25qxx_read_data			(uint8_t * pdata,uint16_t size)
{	
	HAL_SPI_Receive(&hspi1,pdata,size,100);
	return 0;
}

uint8_t		w25qxx_init					(void)
{
	uint16_t device_id = 0;
	/*< SPI初始化 >*/
	
	/*< 唤醒 >*/
	w25qxx_wakeup();
	
	/*< 读ID >*/
	device_id = w25qxx_read_id();
	
	if(W25Q64 != device_id)
		return 1;
		
	return 0;
}

void 		w25qxx_deinit				(void)
{
	/*< 休眠 >*/
	w25qxx_sleep();
	
	/*< SPI逆初始化 >*/
}

void 		w25qxx_sleep				(void)
{
	uint8_t cmds[] = {W25QXX_PowerDown};
	
	/*< 等待BUSY >*/
	w25qxx_wait_busy();
	
	/*< 发送 休眠 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_set_nss(1);
}

void 		w25qxx_wakeup				(void)
{
	uint8_t cmds[] = {W25QXX_ReleasePowerDown};
	
	/*< 发送 唤醒 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_set_nss(1);
}

uint16_t 	w25qxx_read_id				(void)
{
	uint8_t cmds[] = {W25QXX_ManufactDeviceID,0x00,0x00,0x00};
	uint16_t device_id = 0;
	
	/*< 发送 读设备ID 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_write_byte(cmds[1]);
	_w25qxx_write_byte(cmds[2]);
	_w25qxx_write_byte(cmds[3]);
	device_id = _w25qxx_read_byte();
	device_id <<= 8;
	device_id |= _w25qxx_read_byte();
	_w25qxx_set_nss(1);
	
	return device_id;
}

void 		w25qxx_write_status			(uint8_t status)
{
	uint8_t cmds[] = {W25QXX_WriteStatusReg,0};
	
	cmds[1] = status;
	
	/*< 等待BUSY >*/
	w25qxx_wait_busy();
	
	/*< 发送 写状态寄存器 命令 和 状态寄存器值 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_write_byte(cmds[1]);
	_w25qxx_set_nss(1);
}

uint8_t 	w25qxx_read_status			(void)
{
	uint8_t cmds[] = {W25QXX_ReadStatusReg};
	uint8_t status = 0;
	
	/*< 发送 读状态寄存器 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	status = _w25qxx_read_byte();
	_w25qxx_set_nss(1);
	
	return status;
}

void 		w25qxx_wait_busy			(void)
{
	uint8_t status = 0;
	
	do
	{
		status = w25qxx_read_status();
	}while(0x01 == (status & 0x01));
}

void 		w25qxx_write_enable			(void)
{
	uint8_t cmds[] = {W25QXX_WriteEnable};
	
	/*< 发送 写使能 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_set_nss(1);
}

void 		w25qxx_write_disable		(void)
{
	uint8_t cmds[] = {W25QXX_WriteDisable};
	
	/*< 发送 写失能 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_set_nss(1);
}

void 		w25qxx_erase_chip			(void)
{
	uint8_t cmds[] = {W25QXX_ChipErase};
	
	w25qxx_wait_busy();
	
	w25qxx_write_enable();
	
	w25qxx_wait_busy();
	
	/*< 发送 擦除芯片 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_set_nss(1);
	
	w25qxx_wait_busy();
}

void 		w25qxx_erase_sector			(uint32_t addr)
{
	uint8_t cmds[] = {W25QXX_SectorErase,0,0,0};
	
	addr <<= 12;
	cmds[1] = (uint8_t)(addr >> 16);
	cmds[2] = (uint8_t)(addr >> 8);
	cmds[3] = (uint8_t)(addr >> 0);
	
	w25qxx_wait_busy();
	
	w25qxx_write_enable();
	
	w25qxx_wait_busy();
	
	/*< 发送 擦除扇区 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_byte(cmds[0]);
	_w25qxx_write_byte(cmds[1]);
	_w25qxx_write_byte(cmds[2]);
	_w25qxx_write_byte(cmds[3]);
	_w25qxx_set_nss(1);
	
	w25qxx_wait_busy();
	
	w25qxx_write_disable();
	
	w25qxx_wait_busy();
}

void		w25qxx_page_program			(uint32_t addr,
										 uint8_t * pdata,
										 uint16_t size)
{
	uint8_t cmds[] = {W25QXX_PageProgram,0,0,0};
	
	cmds[1] = (uint8_t)(addr >> 16);
	cmds[2] = (uint8_t)(addr >> 8);
	cmds[3] = (uint8_t)(addr >> 0);
	
	w25qxx_wait_busy();
	
	w25qxx_write_enable();
	
	w25qxx_wait_busy();
	
	/*< 发送 页编程 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_data(cmds,4);
	_w25qxx_write_data(pdata,size);
	_w25qxx_set_nss(1);
	
	w25qxx_wait_busy();
}

void 		w25qxx_write_no_check		(uint32_t addr,
										 uint8_t * pdata,
										 uint16_t size)
{
	uint16_t page_remain = 256 - (addr % 256);
	
	if(size <= page_remain)
		page_remain = size;
		
	while(1)
	{
		w25qxx_page_program(addr,pdata,page_remain);
		
		if(size == page_remain)
			break;
			
		pdata += 256;
		addr += 256;
		size -= 256;
		
		if(256 < page_remain)
			page_remain = 256;
		else
			page_remain = size;
	}
}

void 		w25qxx_write				(uint32_t addr,
										 uint8_t * pdata,
										 uint16_t size)
{
	uint32_t secpos = 0;
	uint16_t secoffset = 0;
	uint16_t secremain = 0;
	uint16_t i = 0;
	uint8_t * ptr = _w25qxx_buf;

	/*< 获取扇区索引号 >*/
	secpos = addr / 4096;
	
	/*< 获取扇区相对偏移 >*/
	secoffset = addr % 4096;
	
	/*< 获取扇区剩余相对偏移 >*/
	secremain = 4096 - secoffset;
	
	if(size < secremain)
		secremain = size;
		
	while(1)
	{
		/*< 往回读取4k字节 >*/
		w25qxx_read(secpos * 4096,ptr,4096);
		
		/*< 往回读取的字节不为空(0xff) >*/
		for(i = 0;i < secremain;i++)
		{
			if(0xff != ptr[secoffset + i])
				break;
		}
		
		/*< 需要擦除 >*/
		if(i < secremain)
		{
			/*< 擦除一个扇区 >*/
			w25qxx_erase_sector(secpos);
			for(i = 0;i < secremain;i++)
			{
				ptr[secoffset + i] = pdata[i];
			}
			w25qxx_write_no_check(secpos * 4096,ptr,4096);
		}
		else
			w25qxx_write_no_check(addr,pdata,secremain);
		
		if(size == secremain)
			break;
			
		/*< 扇区索引号+1 >*/
		secpos ++;
		
		/*< 扇区相对偏移为0 >*/
		secoffset = 0;
		
		pdata += secremain;
		addr += secremain;
		size -= secremain;
		if(4096 < secremain)
			secremain = 4096;
		else
			secremain = size;
	}
}

void 		w25qxx_read					(uint32_t addr,
										 uint8_t * pdata,
										 uint16_t size)
{
	uint8_t cmds[] = {W25QXX_ReadData,0,0,0};
	
	cmds[1] = (uint8_t)(addr >> 16);
	cmds[2] = (uint8_t)(addr >> 8);
	cmds[3] = (uint8_t)(addr >> 0);
	
	w25qxx_wait_busy();
	
	/*< 发送 读数据 命令 >*/
	_w25qxx_set_nss(0);
	_w25qxx_write_data(cmds,4);
	_w25qxx_read_data(pdata,size);
	_w25qxx_set_nss(1);
}
/******************************** Functions **********************************/
